package com.ruoyi.aitcommon.utils;

import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import com.alibaba.fastjson.JSONObject;
import com.ruoyi.aitcommon.domain.AitLog;
import com.ruoyi.aitcommon.service.IAitLogService;
import com.ruoyi.aitcommon.vo.FlowContextVO;
import com.ruoyi.common.constant.HttpStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

@Component
public class RestServiceBase {
//	链接超时时间,如果为0则永不超时
	private static final int CONNECT_TIMEOUT = 2 * 1000;
//	调用超时时间,如果为0则永不超时
	private static final int SOCKET_TIMEOUT = 2 * 60 * 1000;

	//	静态方法里边如何使用spring的注入对象
//	参考文档 : https://blog.csdn.net/Jarbein/article/details/117857580
	private static RestServiceBase restServiceBase;
	@Autowired
	private IAitLogService logService;
	@Autowired
	private RuntimeManager runtimeManager;

	@PostConstruct
	public void init(){
		restServiceBase = this;
		restServiceBase.logService = this.logService;
		restServiceBase.runtimeManager=this.runtimeManager;
	}

	private static Date startTime;
	private static Date endTime;

	/**
	 * 调用返回的通用检查
	 * @param response
	 * @param url
	 * @throws Exception
	 */
	private static void checkResponse(HttpResponse response,String url) throws Exception {
		if (response.getStatus() != 200) {
			String msg = "url[%s]调用错误,错误码:[%s],返回消息:[%s]";
			msg = String.format(msg, url, response.getStatus(),response.body());
			throw new Exception(msg);
		}
	}
	
	/**
	 * 构造query参数
	 * @param query
	 * @return query参数的字符串形式
	 */
	protected static String queryParam(Map<String,Object> query) {
		if (query == null || query.isEmpty()) {
			return "";
		}
		List<String> params = new ArrayList<>();
		for (Map.Entry<String, Object> entry : query.entrySet()) {
			String key = entry.getKey();
			Object value = entry.getValue();
			try {
				String encodedKey = URLEncoder.encode(key, StandardCharsets.UTF_8.toString());
				String encodedValue = (value != null)
						? URLEncoder.encode(value.toString(), StandardCharsets.UTF_8.toString())
						: "";

				params.add(encodedKey + "=" + encodedValue);
			} catch (Exception e) {
				// 理论上 UTF-8 总是可用的，但为了安全可捕获
				throw new RuntimeException("Encoding failed", e);
			}
		}
		return StrUtil.join("&", params);
	}
	
	/**
	 * 远程调用的通用处理过程
	 * @param request
	 * @param head
	 * @param body body参数,一般是JSON字符串
	 * @return BODY字符串
	 * @throws Exception 
	 */
	protected static String restInvoke(HttpRequest request,String body, Map<String,Object> head) throws Exception {
		HttpResponse response=null;
		try {
//			设置超时
			request = request.setConnectionTimeout(CONNECT_TIMEOUT)
					.setReadTimeout(SOCKET_TIMEOUT);
//			设置HTTP头部信息
			if (head != null&&head.size()>0) {
				for (String key : head.keySet()) {
					String val = head.get(key).toString();
					request = request.header(key, val);
				}
			}
//			设置BODY信息
			if(StrUtil.isNotBlank(body)) {
//				hutool依据body值,自动绑定content-type
				request=request.body(body);
			}
//			调用前日志
			restLog(request, body, head);
			//远程调用
			startTime=new Date();
			response = request.execute();
			endTime=new Date();
			//返回日志
			LogUtils.httpDebug("HTTP调用返回:\r\n{}" , response.body());
			LogUtils.httpDebug("===========远程调用=============" , response.body());
//			检查返回
			checkResponse(response,request.getUrl());
			return response.body();
		}catch(Exception ex) {
			LogUtils.httpDebug("调用错误,具体见ait-common");
			throw ex;
		}finally {
//			记录日志
			dbLog(request,body,head,response);
			if(restServiceBase!=null&&restServiceBase.runtimeManager!=null){
				restServiceBase.runtimeManager.addRestCall(request,body,head,response);
			}
//			性能日志
			long duration = endTime.getTime() - startTime.getTime();//耗时,毫秒
			if(duration>AITConst.WARN_RUNTIME){
				LogUtils.perfWarn("HTTP调用耗时:[{}]秒,URL:{}",duration/1000,request.getUrl());
			}
		}
	}

	private static void restLog(HttpRequest request, String body, Map<String, Object> head) {
		StringBuilder logstr=new StringBuilder();
		logstr.append(StrUtil.format("===========远程调用=============\r\n", request.getMethod().toString()));
		logstr.append(StrUtil.format("HTTP调用:[{}]\r\n", request.getMethod().toString()));
		logstr.append(StrUtil.format("FLowID:[{}] FlowCode:[{}] FlowName:[{}]\r\n"
				,FlowUtils.getFlowID(),FlowUtils.getFlowCode(),FlowUtils.getFlowName()));
		logstr.append(StrUtil.format("调用地址:{}\r\n", request.getUrl()));
		if(head != null&& head.size()>0) {
			logstr.append(StrUtil.format("HEAD参数:{}\r\n", head.toString()));
		}
		if(StrUtil.isNotBlank(body)) {
			logstr.append(StrUtil.format("BODY参数:\r\n{}", body));
		}
		//日志一次性输出,确保并发调用时,日志不会乱
		LogUtils.httpInfo(logstr.toString());
	}

	/**
	 * 下载url文件到本地
	 * @param url url地址
	 * @param path 存储路径,绝对路径
	 * @return File对象
	 */
	public static File getFile(String url, String path) throws Exception {
		HttpResponse response = HttpRequest.get(url)
				.setConnectionTimeout(CONNECT_TIMEOUT)
				.execute();
		checkResponse(response,url);
		byte[] bodyBytes = response.bodyBytes();
		//hutool文件写入,会自动创建目录
		File file = FileUtil.writeBytes(bodyBytes, path);
		return file;
	}

	/**
	 * http数据库日志
	 * @param request
	 * @param body
	 * @param head
	 */
	public static void dbLog(HttpRequest request,String body
			, Map<String,Object> head,HttpResponse response){
		AitLog aitlog = new AitLog();
		try{
			if(startTime==null) startTime=new Date();
			if(endTime==null) endTime=new Date();
			// ========数据库日志=========//
			// 设置标题
			aitlog.setLogType("HTTP日志");
			aitlog.setOperUrl(request.getUrl());
			aitlog.setReqMethod(request.getMethod().toString());
			aitlog.setApiName("HTTP调用");
			aitlog.setStartDate(new Date());
			aitlog.setStartDate(startTime);
			aitlog.setEndDate(endTime);
			Long run=endTime.getTime() - startTime.getTime();
			aitlog.setRunTime(run);

//        	流程名称，流程ID
			FlowContextVO flowinfo = FlowUtils.getFlowContext();
			if (flowinfo != null) {
				aitlog.setFlowId(flowinfo.getFlowID());
				aitlog.setFlowName(flowinfo.getFlowName());
				aitlog.setFlowCode(flowinfo.getFlowCode());
			}

//			数据
			aitlog.setInParam(body);
			if(head!=null&&head.size()>0){
				JSONObject headjson=new JSONObject(head);
				aitlog.setParField0(headjson.toString());
			}

			if(response!=null){
				aitlog.setRetcode(String.valueOf(response.getStatus()));
				aitlog.setRetParam(response.body());
			}

//			保存数据库
			if(restServiceBase!=null&&restServiceBase.logService!=null){
				restServiceBase.logService.insertAitLog(aitlog);
			}
		}catch (Exception ex){
			LogUtils.error(ex.getMessage(),ex);
		}finally {

		}
	}
}
